//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.Provenance.ProvenanceDataService
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Linq;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.Monitor;
    using Microsoft.Practices.Unity;
    using Microsoft.Research.DataLayer;
    using Microsoft.Research.eResearch.Common.Linq;
    using PM = Microsoft.Research.ProvenanceCommon.Model;
    using SR = Microsoft.Research.DataLayer;

    /// <summary>
    /// This class contains logic for retrieving the data for the provenance screen.
    /// </summary>
    public class DataRetriever
    {
        /// <summary>
        /// Unity Container.
        /// </summary>
        IUnityContainer Container;

        /// <summary>
        /// Provenance DB Connection.
        /// </summary>
        private SR.Connection provenanceConnection;

        /// <summary>
        /// Registry DB Connection.
        /// </summary>
        private SR.Connection registryConnection;

        /// <summary>
        /// Data product service.
        /// </summary>
        private DataProductService dataProductService;

        /// <summary>
        /// Job ID associated with the Data Retriever.
        /// </summary>
        private Guid jobId;

        /// <summary>
        /// This contains the Provenance info store for the current Job ID.
        /// This is used as the cache for the provenance data retrieval system.
        /// </summary>
        private Dictionary<string, SR.ProvenanceInfo> currentItems =
            new Dictionary<string, SR.ProvenanceInfo>();

        /// <summary>
        /// Stores all data products related to the current Job.
        /// </summary>
        private Dictionary<string, List<DataProductItem>> dataProductStore = new Dictionary<string, List<DataProductItem>>();

        /// <summary>
        /// Hold the iterative items in this stack.
        /// </summary>
        private Stack<string> pathItems = new Stack<string>();

        /// <summary>
        /// Hold the iterative iteams and their selected index.
        /// </summary>
        private Dictionary<string, SelectedValueStruct> pathItemsSelectedIndexe = new Dictionary<string, SelectedValueStruct>();

        /// <summary>
        /// Start EventOrder for activity.
        /// </summary>
        private int StartEvent;

        /// <summary>
        /// End EventOrder for activity.
        /// </summary>
        private int EndEvent;

        // Get the breadcrumb.
        public string Breadcrumb { get; set; }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="container">Unity Container</param>
        /// <param name="jobId">Job ID.</param>
        /// <param name="provenanceConnection">Provenance DB Connection</param>
        /// <param name="registryConnection">Registry DB Connection</param>
        public DataRetriever(
            IUnityContainer container,
            Guid jobId,
            SR.Connection provenanceConnection,
            SR.Connection registryConnection)
        {
            this.Container = container;
            this.jobId = jobId;
            this.provenanceConnection = provenanceConnection;
            this.registryConnection = registryConnection;

            // Update the Job details into cache
            this.GetJob();
        }

        /// <summary>
        /// Instance of current job.
        /// </summary>
        public Job CurrentJob { get; set; }

        /// <summary>
        /// This function is used to retrive all the data products related to JOB.
        /// </summary>
        /// <returns></returns>
        public Collection<DataProductItem> GetDataProducts()
        {
            // Instantiate Data products.
            this.dataProductService = this.Container.Resolve<DataProductService>();
            this.dataProductStore = new Dictionary<string, List<DataProductItem>>();

            Collection<DataProductItem> DataProductItemCollection = new Collection<DataProductItem>();
            try
            {
                List<DataProduct> dataProductList = new List<DataProduct>();

                this.CurrentJob.ActivityInstances[0].Outputs.ForEach(dataProduct =>
                {
                    if (!dataProduct.IsFinal)
                        dataProductList.Add(dataProduct);
                });

                foreach (DataProduct dataProduct in dataProductList)
                {
                    DataProductItem dpItem = DataProductService.GenerateDataProductItem(dataProduct);
                    if (dpItem != null)
                    {
                        DataProductItemCollection.Add(dpItem);

                        if (this.dataProductStore.ContainsKey(dataProduct.CreatorActivity.Name))
                        {
                            List<DataProductItem> activityDataProductList = this.dataProductStore[dataProduct.CreatorActivity.Name];
                            activityDataProductList.Add(dpItem);
                        }
                        else
                        {
                            List<DataProductItem> newDataProductList = new List<DataProductItem>();
                            newDataProductList.Add(dpItem);
                            this.dataProductStore.Add(dataProduct.CreatorActivity.Name, newDataProductList);
                        }
                    }
                }
            }
            catch (RecordNotFoundException exception)
            {
                TridentErrorHandler.HandleUIException(exception);

                // Ignore Exception: No Data Products present.
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleUIException(exception);
                TridentMessageBox.ShowTridentErrorMessageBox(ProvenanceResourceManager.GetString("ErrorDataProductRetreive"));
            }

            return DataProductItemCollection;
        }

        /// <summary>
        /// This function is used to get the data poduct related to activity.
        /// </summary>
        /// <param name="model">Base model.</param>
        /// <param name="breadcrumb">Breadcrumb text.</param>
        /// <returns>DataProduct items.</returns>
        public Collection<DataProductItem> GetDataProducts(BaseModel model, ref string breadcrumb)
        {
            this.Breadcrumb = string.Empty;
            GetProvenanceDetails(model);
            breadcrumb = this.Breadcrumb;

            if ((this.dataProductService == null && !this.dataProductStore.ContainsKey(model.UniqueId)) ||
                this.dataProductStore.Count < 1)
            {
                this.GetDataProducts();
            }

            if (this.dataProductStore.ContainsKey(model.UniqueId))
            {
                Collection<DataProductItem> dataProductItems = new Collection<DataProductItem>();
                foreach (DataProductItem dataProductItem in this.dataProductStore[model.UniqueId])
                {
                    DataProduct dataProduct = dataProductItem.OriginalDataProduct;
                    foreach (DataProductDetail dataProductDetail in dataProduct.DataProductDetails)
                    {
                        // Check if the selected item EventOrder is between the Start and End range.
                        if (((pathItemsSelectedIndexe.Count == 0) || (EndEvent == 0 && dataProductDetail.EventOrder > StartEvent) ||
                            (StartEvent == 0 && dataProductDetail.EventOrder < EndEvent) ||
                            (dataProductDetail.EventOrder > StartEvent && dataProductDetail.EventOrder < EndEvent)) &&
                            (!dataProductItems.Contains(dataProductItem))
                            )
                        {
                            dataProductItems.Add(dataProductItem);
                        }
                    }
                }

                return dataProductItems;
            }

            return null;
        }

        /// <summary>
        /// This function is used to get workflow summary data.
        /// </summary>
        /// <returns>WorkflowSummaryModel.</returns>
        public PM.WorkflowSummaryModel GetWorkflowSummary()
        {
            PM.WorkflowSummaryModel SummaryModel = new PM.WorkflowSummaryModel();
            try
            {
                if (this.CurrentJob != null)
                {
                    if (this.CurrentJob.ActivityInstances.Count > 0)
                    {
                        if (this.CurrentJob.ActivityInstances[0].Activity.Children.Count > 0)
                        {
                            SummaryModel.Name = this.CurrentJob.ActivityInstances[0].Activity.Name;
                            SummaryModel.Version = this.CurrentJob.ActivityInstances[0].Activity.Version;
                        }
                    }

                    SummaryModel.AuthoredBy = this.CurrentJob.Created.User;
                    SummaryModel.JobName = this.CurrentJob.Name;
                    SummaryModel.ScheduledBy = this.CurrentJob.ScheduledBy.Name;
                    SummaryModel.Node = this.CurrentJob.Machines[0].Name;
                    SummaryModel.StartDateTime = this.CurrentJob.StartTime.ToString(CultureInfo.CurrentCulture);

                    DateTime startTime = this.CurrentJob.StartTime;
                    DateTime endTime = this.CurrentJob.StopTime;
                    TimeSpan duration = endTime - startTime;
                    SummaryModel.Duration = string.Format("{0}:{1}:{2}:{3}:{4}",
                        duration.Days.ToString(CultureInfo.InvariantCulture),
                        duration.Hours.ToString(CultureInfo.InvariantCulture),
                        duration.Minutes.ToString(CultureInfo.InvariantCulture),
                        duration.Seconds.ToString(CultureInfo.InvariantCulture),
                        duration.Milliseconds.ToString(CultureInfo.InvariantCulture));
                }
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000009,
                    ProvenanceResourceManager.GetString("ErrorWFSummaryRetreive"));
            }
            return SummaryModel;
        }

        /// <summary>
        /// Gets the workflow versions.
        /// </summary>
        /// <returns></returns>
        public Collection<PM.VersionHistoryModel> GetWorkflowVersions()
        {
            Collection<PM.VersionHistoryModel> versions = new Collection<PM.VersionHistoryModel>();
            Activity workflow = null;
            try
            {
                if ((this.CurrentJob != null) && (this.CurrentJob.ActivityInstances.Count > 0))
                {
                    workflow = this.CurrentJob.ActivityInstances[0].Activity;

                    PM.VersionHistoryModel currentVersion = new PM.VersionHistoryModel(workflow);
                    if (currentVersion != null)
                    {
                        versions.Add(currentVersion);
                    }

                    RetreiveOlderVersions(versions, workflow);

                    RetreiveNewerVersions(versions, workflow);

                    Collection<PM.VersionHistoryModel> sortedVersions = new Collection<PM.VersionHistoryModel>();
                    versions.OrderByDescending(version => version.Version).ForEach(version => sortedVersions.Add(version));

                    versions = sortedVersions;
                }
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000017,
                    ProvenanceResourceManager.GetString("ErrorWorkflowVersionsRetreive"));
            }
            return versions;
        }

        /// <summary>
        /// Retreives the older versions.
        /// </summary>
        /// <param name="versions">The versions.</param>
        /// <param name="workflow">The workflow.</param>
        private static void RetreiveOlderVersions(Collection<PM.VersionHistoryModel> versions, Activity workflow)
        {
            if (workflow.OlderVersions != null)
            {
                foreach (ActivityVersion older in workflow.OlderVersions)
                {
                    PM.VersionHistoryModel olderModel = new PM.VersionHistoryModel(older.Activity_1);
                    if (olderModel != null)
                    {
                        versions.Add(olderModel);
                    }

                    RetreiveOlderVersions(versions, older.Activity_1);
                }
            }
        }

        /// <summary>
        /// Retreives the newer versions.
        /// </summary>
        /// <param name="versions">The versions.</param>
        /// <param name="workflow">The workflow.</param>
        private static void RetreiveNewerVersions(Collection<PM.VersionHistoryModel> versions, Activity workflow)
        {
            if (workflow.NewerVersions != null)
            {
                foreach (ActivityVersion newer in workflow.NewerVersions)
                {
                    PM.VersionHistoryModel newerModel = new PM.VersionHistoryModel(newer.Activity_2);
                    if (newerModel != null)
                    {
                        versions.Add(newerModel);
                    }

                    RetreiveNewerVersions(versions, newer.Activity_2);
                }
            }
        }

        /// 
        /// <summary>
        /// Retrieve the processing status information for the activity.
        /// </summary>
        /// <param name="activityUniqueID">BaseModel.</param>
        /// <param name="breadcrumb">The breadcrumb.</param>
        /// <returns>Collection of processing status data.</returns>
        public Collection<PM.ProcessingStatusModel> GetProcessingStatus(BaseModel model, ref string breadcrumb)
        {
            Collection<PM.ProcessingStatusModel> processingStatusCollection = new Collection<PM.ProcessingStatusModel>();
            try
            {
                this.Breadcrumb = string.Empty;
                GetProvenanceDetails(model);
                breadcrumb = this.Breadcrumb;

                if (this.currentItems.ContainsKey(model.UniqueId))
                {
                    SR.ProvenanceInfo info = this.currentItems[model.UniqueId];

                    foreach (SR.ProvenanceDetail provenanceDetail in info.ProvenanceDetails)
                    {
                        foreach (SR.ProcessingStatus processingStatus in provenanceDetail.ProcessingStatuss)
                        {
                            // Check if the selected item EventOrder is between the Start and End range.
                            if (((EndEvent == 0 && processingStatus.EventOrder > StartEvent && processingStatus.Name.Equals(model.UniqueId, StringComparison.OrdinalIgnoreCase)) ||
                                (StartEvent == 0 && processingStatus.EventOrder < EndEvent && processingStatus.Name.Equals(model.UniqueId, StringComparison.OrdinalIgnoreCase)) ||
                                (processingStatus.EventOrder > StartEvent && processingStatus.EventOrder < EndEvent && processingStatus.Name.Equals(model.UniqueId, StringComparison.OrdinalIgnoreCase))) && processingStatus.Events != null)
                            {
                                processingStatusCollection.Add(new PM.ProcessingStatusModel()
                                   {
                                       ActivityName = model.UniqueId,
                                       Details = processingStatus.Details,
                                       Event = processingStatus.Events,
                                       Time = TimeZone.CurrentTimeZone.ToLocalTime(processingStatus.PublishedTime)
                                   });
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000007,
                    ProvenanceResourceManager.GetString("ErrorProcessingStatusRetreive"));
            }

            return processingStatusCollection;
        }

        /// <summary>
        /// Retrieve the parameter information for the activity specified.
        /// </summary>
        /// <param name="activityUniqueID">The activity name.</param>
        /// <returns>Collection of property data.</returns>
        public Collection<PM.ActivityProperty> GetParameters(BaseModel model, ref string breadcrumb)
        {
            this.Breadcrumb = string.Empty;

            GetProvenanceDetails(model);

            breadcrumb = this.Breadcrumb;

            Collection<PM.ActivityProperty> parametersCollection = new Collection<PM.ActivityProperty>();
            try
            {
                if (this.currentItems.ContainsKey(model.UniqueId))
                {
                    SR.ProvenanceInfo info = this.currentItems[model.UniqueId];

                    foreach (SR.ProvenanceDetail provenanceDetail in info.ProvenanceDetails)
                    {

                        foreach (SR.InputOutput property in provenanceDetail.InputOutputs)
                        {
                            if (((EndEvent == 0 && property.EventOrder > StartEvent && property.Name.Equals(model.UniqueId, StringComparison.OrdinalIgnoreCase)) ||
                                (StartEvent == 0 && property.EventOrder < EndEvent && property.Name.Equals(model.UniqueId, StringComparison.OrdinalIgnoreCase)) ||
                                (property.EventOrder > StartEvent && property.EventOrder < EndEvent && property.Name.Equals(model.UniqueId, StringComparison.OrdinalIgnoreCase))))
                            {
                                if (null == parametersCollection.FirstOrDefault(ioParameter =>
                                        ioParameter.Name.Equals(property.PropertyName) &&
                                        ioParameter.Value.Equals(property.PropertyValue) &&
                                        ioParameter.Direction.Equals(property.Direction)))

                                    if (!string.IsNullOrEmpty(property.PropertyValue))
                                    {
                                        PM.ActivityProperty activityProperty = parametersCollection.FirstOrDefault(param => param.Name.Equals(property.PropertyName, StringComparison.OrdinalIgnoreCase) && param.Direction == property.Direction);
                                        if (activityProperty == null)
                                        {
                                            // Add to collection if its not in the collection
                                            parametersCollection.Add(new PM.ActivityProperty
                                               {
                                                   Name = property.PropertyName,
                                                   Value = property.PropertyValue,
                                                   Direction = property.Direction,
                                                   EventOrder = property.EventOrder
                                               });
                                        }
                                        else
                                        {
                                            // Update the collection if its not in the collection and if the event Order is greater than stored event order
                                            if (activityProperty.EventOrder < property.EventOrder)
                                            {
                                                activityProperty.Value = property.PropertyValue;
                                            }
                                        }
                                    }
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000006,
                    ProvenanceResourceManager.GetString("ErrorIOParametersRetreive"));
            }
            return parametersCollection;

        }

        /// <summary>
        /// Retrieve the performance information for the activity.
        /// </summary>
        /// <param name="activityUniqueID">The activity unique name.</param>
        /// <returns>The collection of performance data.</returns>
        public Collection<PM.PerformanceModel> GetPerformanceInformation(BaseModel model, ref string breadcrumb)
        {
            this.Breadcrumb = string.Empty;

            GetProvenanceDetails(model);

            breadcrumb = this.Breadcrumb;

            Collection<PM.PerformanceModel> performanceCollection = new Collection<PM.PerformanceModel>();
            try
            {
                if (this.currentItems.ContainsKey(model.UniqueId))
                {
                    SR.ProvenanceInfo info = this.currentItems[model.UniqueId];

                    foreach (SR.ProvenanceDetail provenanceDetail in info.ProvenanceDetails)
                    {
                        foreach (SR.PerformanceStat performance in provenanceDetail.PerformanceStats)
                        {
                            performanceCollection.Add(new PM.PerformanceModel()
                                {
                                    Average = performance.Average,
                                    Maximum = performance.Maximum,
                                    Minimum = performance.Minimum,
                                    PerformanceCounter = performance.Name
                                });
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000005,
                    ProvenanceResourceManager.GetString("ErrorPerformanceRetreive"));
            }
            return performanceCollection;
        }

        /// <summary>
        /// Retrieve the provenance info for the job.
        /// and update it in the cache.
        /// </summary>
        public void GetProvenanceInfo()
        {
            try
            {
                List<ProvenanceInfo> provInfos = SR.ProvenanceInfo.LoadAll(this.registryConnection);

                foreach (ProvenanceInfo provInfo in provInfos)
                {
                    if (provInfo.JobId.ToString().Equals(this.jobId.ToString(), StringComparison.CurrentCultureIgnoreCase) &&
                        provInfo.ActivityName != null &&
                        this.currentItems != null &&
                        !this.currentItems.ContainsKey(provInfo.ActivityName))
                    {
                        this.currentItems.Add(provInfo.ActivityName, provInfo);
                    }
                }
                //.ForEach(provDetail =>
                //{
                //    if (provDetail.ProvenanceInfo.ActivityName != null && this.currentItems != null && !this.currentItems.ContainsKey(provDetail.ProvenanceInfo.ActivityName))
                //        this.currentItems.Add(provDetail.ProvenanceInfo.ActivityName, provDetail);
                //});
            }
            catch (RecordNotFoundException exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000001,
                    ProvenanceResourceManager.GetString("ErrorNoProvenanceData"));
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000002,
                    ProvenanceResourceManager.GetString("ErrorProvenanceRetreive"));
            }
        }

        /// <summary>
        /// Gets the workflow details from the job.
        /// </summary>
        public TridentWorkflowModel GetWorkflow()
        {
            TridentWorkflowModel workflowModel = null;
            try
            {
                SR.Activity workflow = this.CurrentJob.ActivityInstances[0].Activity;

                ModelFactory modelFactory = new ModelFactory(null, null);

                workflowModel = new TridentWorkflowModel(workflow, modelFactory);

                workflowModel.SetWorkflowParameters(this.CurrentJob);
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000008,
                    ProvenanceResourceManager.GetString("ErrorWFDataRetreive"));
            }

            return workflowModel;
        }

        /// <summary>
        /// Gets all the required inputs of a JOB.
        /// </summary>
        public Collection<PM.ActivityProperty> GetWorkflowInputs()
        {
            Collection<PM.ActivityProperty> workflowInputModel = new Collection<PM.ActivityProperty>();
            try
            {
                foreach (ActivityInstanceParameter parameter in this.CurrentJob.ActivityInstances[0].ActivityInstanceParameters)
                {
                    PM.ActivityProperty property = new PM.ActivityProperty()
                    {
                        Name = parameter.ParameterAssignment.ActivityParameter.Name,
                        Value = parameter.ParamValue,
                        Direction = parameter.ParameterAssignment.ActivityParameter.Direction,
                        ActivityName = parameter.ParameterAssignment.Instance.Name
                    };

                    workflowInputModel.Add(property);
                }
            }
            catch (RecordNotFoundException exception)
            {
                TridentErrorHandler.HandleUIException(exception);
                // Ignore Exception: No wokflow inputs present.
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000008,
                    ProvenanceResourceManager.GetString("ErrorWFRequiredInputRetreive"));
            }

            return workflowInputModel;
        }

        /// <summary>
        /// Gets all the required inputs of a JOB.
        /// </summary>
        public Collection<PM.ActivityProperty> GetWorkflowOutputs()
        {
            Collection<PM.ActivityProperty> workflowOutputs = new Collection<PM.ActivityProperty>();
            try
            {
                if (this.dataProductService == null)
                {
                    this.dataProductService = this.Container.Resolve<DataProductService>();
                }

                this.CurrentJob.ActivityInstances[0].Outputs.ForEach(dataProduct =>
                {
                    if (dataProduct.IsFinal)
                    {
                        DataProductItem dpItem = DataProductService.GenerateDataProductItem(dataProduct);
                        if (dpItem != null)
                        {
                            PM.ActivityProperty outputProperty = new PM.ActivityProperty()
                            {
                                Name = dataProduct.CreatorParameter.ActivityParameter.Name,
                                ActivityName = dataProduct.CreatorParameter.Instance.Name,
                                Direction = dataProduct.CreatorParameter.ActivityParameter.Direction,
                                Data = dpItem
                            };

                            workflowOutputs.Add(outputProperty);
                        }
                    }
                });
            }
            catch (RecordNotFoundException exception)
            {
                TridentErrorHandler.HandleUIException(exception);
                // Ignore Exception: No wokflow outputs present.
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000008,
                    exception.Message);
            }

            return workflowOutputs;
        }

        /// <summary>
        /// This function is used to retrieve the job from registry
        /// and assign it to the currentJOb property.
        /// </summary>
        private void GetJob()
        {
            try
            {
                this.CurrentJob = Job.Load(this.jobId, this.registryConnection);
            }
            catch (RecordNotFoundException exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000003,
                    ProvenanceResourceManager.GetString("ErrorJobNotFound"));
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000004,
                    ProvenanceResourceManager.GetString("ErrorWFDataRetreive"));
            }
        }

        /// <summary>
        /// Gets the iteration values.
        /// </summary>
        /// <param name="acitivityUniqueId">The acitivity unique id.</param>
        /// <returns></returns>
        public List<string> GetIterationValues(string activityUniqueId)
        {
            List<ProvenanceDetail> provDetails = null;
            List<string> iterations = new List<string>();
            try
            {
                ProvenanceInfo.ISearch searchQuery = ProvenanceInfo.ISearch.Create();
                searchQuery.Query = ProvenanceInfo.ISearch.AND(
                    ProvenanceInfo.ISearch.JobId(StringField.Condition.Equals, this.jobId.ToString()),
                    ProvenanceInfo.ISearch.ActivityName(StringField.Condition.Equals, activityUniqueId));

                List<ProvenanceInfo> provenanceInfos = ProvenanceInfo.Search(searchQuery, this.registryConnection);

                if (null != provenanceInfos)
                {
                    ProvenanceDetail.ISearch searchQuery2 = ProvenanceDetail.ISearch.Create();
                    searchQuery2.Query = ProvenanceDetail.ISearch.AND(
                        ProvenanceDetail.ISearch.ProvenanceInfo(ObjectFieldBase.Condition.Equals, provenanceInfos[0]),
                        ProvenanceDetail.ISearch.CurrentIndex(StringField.Condition.NotEquals, string.Empty));

                    provDetails = ProvenanceDetail.Search(searchQuery2, this.registryConnection);

                    provDetails = provDetails.OrderBy(provenaceDetail => provenaceDetail.StartEventOrder).ToList();

                    foreach (ProvenanceDetail provDetail in provDetails)
                    {
                        if (!string.IsNullOrEmpty(provDetail.CurrentIndex) && !iterations.Contains(provDetail.CurrentIndex))
                        {
                            iterations.Add(provDetail.CurrentIndex);
                        }
                    }
                }
            }
            catch (RecordNotFoundException)
            {
            }
            catch (Exception)
            {
            }

            return iterations;
        }

        /// <summary>
        /// Gets the iterative activities.
        /// </summary>
        /// <param name="selectedActivity">The selected activity.</param>
        private void GetIterativeActivities(BaseModel selectedActivity)
        {
            if (selectedActivity is IIterativeInterface)
            {
                string activity = string.Empty;
                string label = string.Empty;

                switch (selectedActivity.GetType().Name)
                {
                    case "ForActivityCompositeModel":
                        activity = "For";
                        label = (selectedActivity as CompositeActivityModel).UniqueId;
                        break;
                    case "ReplicatorActivityCompositeModel":
                        activity = "Replicator";
                        label = (selectedActivity as CompositeActivityModel).Children[0].UniqueId;
                        break;
                }

                if (!pathItems.Contains(label))
                    pathItems.Push(label);

                if (!pathItemsSelectedIndexe.Keys.Contains(label))
                {
                    SelectedValueStruct selectedValues = new SelectedValueStruct();
                    selectedValues.SelectedValue = (selectedActivity as CompositeActivityModel).SelectedValue;
                    selectedValues.NextSelectedValue = (selectedActivity as CompositeActivityModel).NextSelectedValue;

                    pathItemsSelectedIndexe.Add(label, selectedValues);

                    if (string.IsNullOrEmpty(this.Breadcrumb))
                    {
                        this.Breadcrumb = activity + " Iteration " + pathItemsSelectedIndexe[label].SelectedValue;
                    }
                    else
                    {
                        this.Breadcrumb = activity + " Iteration " + pathItemsSelectedIndexe[label].SelectedValue + " > " + this.Breadcrumb;
                    }
                }
            }

            if (null == selectedActivity.ParentModel)
                return;

            GetIterativeActivities(selectedActivity.ParentModel);
        }

        /// <summary>
        /// Sets the iterative range.
        /// </summary>
        private void SetRange()
        {
            // Reset the range.
            StartEvent = 0;
            EndEvent = 0;

            while (pathItems.Count > 0)
            {
                // Pop the path item.
                string IterativeActivity = pathItems.Pop();

                // Get the index for poped item.
                SelectedValueStruct SelectedValue = pathItemsSelectedIndexe[IterativeActivity];

                // Set Start range.
                SetStartEvent(IterativeActivity, SelectedValue.SelectedValue);

                // Set End range.
                SetEndEvent(IterativeActivity, SelectedValue.NextSelectedValue);
            }
        }

        /// <summary>
        /// Sets the start event.
        /// </summary>
        /// <param name="IterativeActivity">The iterative activity.</param>
        /// <param name="selectedValue">The selected value.</param>
        private void SetStartEvent(string IterativeActivity, string selectedValue)
        {
            List<ProvenanceDetail> provDetail = this.GetProvenanceDetailForIteration(IterativeActivity, selectedValue);//For1_2 ; For2_3; For3_2

            if ((null != provDetail) && provDetail.Count > 0)
            {
                foreach (ProvenanceDetail detail in provDetail)
                {
                    if (StartEvent == 0 ||
                        (detail.StartEventOrder >= StartEvent && detail.StartEventOrder <= EndEvent) ||
                        (EndEvent == 0 && detail.StartEventOrder >= StartEvent))//12 ; 
                    {
                        StartEvent = detail.StartEventOrder;
                    }
                }
            }
        }

        /// <summary>
        /// Sets the end event.
        /// </summary>
        /// <param name="IterativeActivity">The iterative activity.</param>
        /// <param name="selectedValue">The selected value.</param>
        private void SetEndEvent(string IterativeActivity, string selectedValue)
        {
            List<ProvenanceDetail> provDetail = this.GetProvenanceDetailForIteration(IterativeActivity, selectedValue);//For1_2 ; For2_3; For3_2

            if ((null != provDetail) && provDetail.Count > 0)
            {
                foreach (ProvenanceDetail detail in provDetail)
                {
                    if ((EndEvent == 0 && StartEvent <= detail.StartEventOrder) ||
                        (detail.StartEventOrder >= StartEvent && detail.StartEventOrder <= EndEvent)) //12 ; 
                    {
                        EndEvent = detail.StartEventOrder;
                    }
                }
            }
        }

        /// <summary>
        /// Gets the provenance details.
        /// </summary>
        /// <param name="model">The model.</param>
        private void GetProvenanceDetails(BaseModel model)
        {
            // Reset the stack.
            pathItems.Clear();
            pathItemsSelectedIndexe.Clear();

            if (null != (model as BaseModel).ParentModel)
            {
                GetIterativeActivities((model as BaseModel).ParentModel);
            }

            SetRange();
        }

        /// <summary>
        /// Gets the provenance detail for iteration.
        /// </summary>
        /// <param name="acitivityUniqueId">The acitivity unique id.</param>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        private List<ProvenanceDetail> GetProvenanceDetailForIteration(string acitivityUniqueId, string index)
        {
            List<ProvenanceDetail> provDetails = null;
            try
            {
                // Get the ProvenanceInfo value from currentItems.
                ProvenanceInfo info = currentItems[acitivityUniqueId];

                ProvenanceDetail.ISearch searchQuery = ProvenanceDetail.ISearch.Create();
                searchQuery.Query = ProvenanceDetail.ISearch.AND(
                    ProvenanceDetail.ISearch.ProvenanceInfo(ObjectFieldBase.Condition.Equals, info),
                    ProvenanceDetail.ISearch.CurrentIndex(StringField.Condition.Equals, index));

                provDetails = ProvenanceDetail.Search(searchQuery, this.registryConnection);
            }
            catch (RecordNotFoundException)
            {
            }
            catch (Exception)
            {
            }

            return provDetails;
        }
    }
}
